/*
 * LectorAritmetico.cpp
 *
 *  Created on: 06/04/2012
 *      Author: lucas
 */

#include "../hdr/LectorAritmetico.h"

#include <stdio.h>
#include <iostream>
#include <string.h>
#include "../hdr/FuncionesAritmeticas.h"

using namespace std;

unsigned int LectorAritmetico::tamanioBuffer = 1024;
unsigned int LectorAritmetico::precision = 16;

LectorAritmetico::LectorAritmetico(FILE* archivo) :
		archivo(archivo), piso(0), techo((1 << precision) - 1), limite(
				1 << (precision - 1)), underflow(0), posicion(0), contadorBuffer(
				0), contadorSubBuffer(0) {
	buffer = new unsigned char[tamanioBuffer];
	this->leerBufferDeArchivo();
	this->leerBitsDeBuffer(precision);
	archivoDebug = fopen("debug.dbg", "rb");
}

LectorAritmetico::~LectorAritmetico() {
	fclose(archivoDebug);
	delete[] buffer;
}

// Recibe el array de largos de los subintervalos, la cantidad de símbolo (largo
// del array y el largo total del intervalo. Devuelve el símbolo correspondiente.
unsigned short LectorAritmetico::leerSimbolo(unsigned short* largos,
		unsigned short total, unsigned short cantidad) {
//	Cosas de debug
//	unsigned int pisoDebug, techoDebug, leyo = 1;
//	leyo &= fread(&pisoDebug, sizeof(unsigned int), 1, archivoDebug);
//	leyo &= fread(&techoDebug, sizeof(unsigned int), 1, archivoDebug);
	unsigned int largo_actual = this->techo - this->piso + 1;
//	unsigned int posicionObjetivo = (total * (posicion - this->piso + 1))
//			/ largo_actual;
	unsigned int largo = 0;
	unsigned int piso = 0;
	unsigned short simbolo;
	// Va fijándose para cada símbolo si la posición está entre el piso y
	// el techo y frena al encontrarlo
	for (simbolo = 0; simbolo < cantidad; simbolo++) {
		piso += largo;
		largo = largos[simbolo];
		this->techo = this->piso + (largo_actual * (piso + largo)) / total - 1;
		if (this->techo >= posicion) {
			break;
		}
	}
	this->techo = this->piso + (largo_actual * (piso + largo)) / total - 1;
	this->piso += (largo_actual * piso) / total;
	// Ahora renormaliza piso y piso al igual que el emisor, y hace lo mismo con
	// la posición (insertando los bits leídos del archivo a medida que
	// renormaliza).
	this->renormalizar();
	return simbolo;
}

// Renormaliza el piso y el techo actual, haciendo lo mismo con la posición.
void LectorAritmetico::renormalizar() {
	unsigned int bitsEmitidos = 0;
	unsigned int emision = 0;
	unsigned int ultimoUnderflow = underflow;
	// Bits en común: renormaliza y cuenta cuántos bits son eliminados
	bitsEmitidos = FuncionesAritmeticas::emitirBits(piso, techo, precision,
			underflow, emision);
	if (bitsEmitidos > 0) {
		// Elimina los bits renormalizados de posicion
		FuncionesAritmeticas::sacarPrimerosBits(posicion, precision, 0,
				bitsEmitidos - ultimoUnderflow);
		// Lee la cantidad de bits eliminados desde el buffer
		this->leerBitsDeBuffer(bitsEmitidos - ultimoUnderflow);
	}
	// Actualiza el underflow al igual que el emisor
	ultimoUnderflow = FuncionesAritmeticas::actualizarUnderflow(piso, techo,
			precision);
	underflow += ultimoUnderflow;
	// Saca los bits de underflow eliminados de la posicion (lo hace de a
	// precision bits como máximo por si el underflow es demasiado grande).
	while (ultimoUnderflow) {
		int primerBit = posicion >= limite;
		int aux = (ultimoUnderflow % precision) ?
						(ultimoUnderflow % precision) : precision;
		FuncionesAritmeticas::sacarPrimerosBits(posicion, precision, 0,
				aux + 1);
		posicion >>= 1;
		posicion |= primerBit ? limite : 0;
		// Lee la cantidad de bits eliminados desde el buffer
		this->leerBitsDeBuffer(aux);
		ultimoUnderflow -= aux;
	}
}

// Lee la cantidad de bits del buffer y los inserta en posicion.
void LectorAritmetico::leerBitsDeBuffer(unsigned int cantidadDeBits) {
	unsigned int bitsAInsertar = 0;
	unsigned char caracterActual = buffer[contadorBuffer];
	// Shifteamos los bits que ya fueron insertados
	caracterActual <<= contadorSubBuffer;
	while (cantidadDeBits > 0) {
		cantidadDeBits--;
		bitsAInsertar <<= 1; // Espacio para un nuevo bit
		// Se fija el valor del bit en la posición 7 y la copia en bitsAInsertar
		// (si era 0 entonces no hace falta copiarlo porque queda igual).
		if ((caracterActual & 0x80) != 0)
			bitsAInsertar |= 1;
		// Pasamos al siguiente bit
		caracterActual <<= 1;
		contadorSubBuffer++;
		// Si ya leímos los 8 bits del byte actual, pasamos al siguiente.
		if (contadorSubBuffer >= 8) {
			contadorBuffer++;
			if (contadorBuffer >= tamanioBuffer) // Si leímos todo el buffer
				this->leerBufferDeArchivo();
			caracterActual = buffer[contadorBuffer];
			contadorSubBuffer = 0;
		}
	}
	posicion |= bitsAInsertar; // Copiamos los bits a la posición
}

// Lee el buffer del archivo (notar que si no hay más bytes, los siguientes
// bits del número gigantes son todos ceros, por eso hacemos memset).
void LectorAritmetico::leerBufferDeArchivo() {
	memset(buffer, 0, tamanioBuffer * sizeof(char));
	fread(buffer, sizeof(char), tamanioBuffer, archivo);
	contadorBuffer = 0;
}

